/* 
 * File:   mpi_ub6.cpp
 * Author: sergey
 *
 * Created on 14 Декабрь 2009 г., 19:05
 *
 * Эта программа используется для запуска на кластере с использованием
 * OpenMPI. Она будет перебирать необходимые функции сложности не меньше
 * MinComplication.
 */

#include "DefineConfiguration.h"
#include "LPLibrary.h"
#include "LibraryInterface.h"
#include "NLLibrary.h"
#include <iostream>
#include <sstream>
#include <mpi/mpi.h>
#include <vector>
#include <signal.h>
#include <unistd.h>
#include <fstream>
#include <omp.h>

const UInt8 f1Complication = 9;
const UInt8 f2Complication = 9;
const UInt8 fComplication = 14;
const UInt32 n = 5;
typedef LibraryInfo<n> CurLibInfo;

int rank = 0, size = 1;

int main(int argc, char** argv) {
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Comm_size(MPI_COMM_WORLD,&size);

    std::ostringstream filename;

    MPI_Status state;

    NLLibrary<CurLibInfo> nllib;

    filename << LIBRARYDIRECTORIES << "/nllibs/nllibrary_" << CurLibInfo::n << ".bin";
    nllib.load(filename.str());
    filename.str("");


    if (rank == 0) {
        std::ofstream resultFile;
        resultFile.open("result.bin", std::ios::out | std::ios::binary);
        
        std::cout << "Starting task generator." << std::endl;
        filename.str("");
        filename << LIBRARYDIRECTORIES << "/lpblibs/lplibrary_" << CurLibInfo::n << ".bin";
        LPLibrary<CurLibInfo> lplib;
        lplib.load(filename.str());
        std::vector<UInt64> funcs;
        funcs.resize(size - 1);
        UInt64 busy = 1;                
       
        for(std::map<UInt64, UInt8>::iterator i = lplib.bfclass.begin(); i != lplib.bfclass.end(); i++)            
            if (i->second == f1Complication) {
                std::cout << "Process lpclass " << std::hex << (i->first) << std::endl;
                for(UInt64 f = 0; f < CurLibInfo::fC; f++) {
                    UInt8 fs1;
                    nllib.get(f, fs1);
                    if (fs1 == f2Complication) {
                        UInt64 nf = (i->first << 32) ^ f;
                        if (busy == size) {
                            UInt8 fc;                            
                            MPI_Recv(&fc, 1, MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, 3, MPI_COMM_WORLD, &state);
                            if (fc == fComplication) {
                                resultFile.write((char*)&funcs[state.MPI_SOURCE - 1], sizeof(UInt64));
                            }
                            std::cout << "Function " << std::hex <<  funcs[state.MPI_SOURCE - 1] << " has complication " << std::dec << (int)fc << "." << std::endl;
                            funcs[state.MPI_SOURCE - 1] = nf;                         
                            MPI_Send(&funcs[state.MPI_SOURCE - 1], 1, MPI_UNSIGNED_LONG_LONG, state.MPI_SOURCE, 1, MPI_COMM_WORLD);
                        } else {
                            for(size_t j = 0; j < funcs.size(); j++)
                                if (funcs[j] == 0) {
                                    funcs[j] = nf;
                                    MPI_Send(&funcs[j], 1, MPI_UNSIGNED_LONG_LONG, j + 1, 1, MPI_COMM_WORLD);
                                    busy++;
                                    break;
                                }
                        }
                    }
                }
            }
        for(; busy != 1; busy--) {
            UInt8 fc;
            MPI_Recv(&fc, 1, MPI_UNSIGNED_CHAR, MPI_ANY_SOURCE, 3, MPI_COMM_WORLD, &state);
            if (fc == fComplication) {
                resultFile.write((char*)&funcs[state.MPI_SOURCE - 1], sizeof(UInt64));
            }
            std::cout << "Function " << std::hex <<  funcs[state.MPI_SOURCE - 1] << " has complication " << std::dec << (int)fc << "." << std::endl;
            MPI_Send(&busy, 1, MPI_UNSIGNED_LONG_LONG, state.MPI_SOURCE, 2, MPI_COMM_WORLD);
        }        
        resultFile.close();
        std::cout << "Task generator ending work." << std::endl;
    } else {
        std::cout << "Starting processor: " << rank << std::endl;

        std::ifstream fsfile;
        std::vector<std::vector<UInt64> > fs;
        fs.resize(5);
        for(size_t i = 0; i < 5; i++) {
            UInt64 size;
            filename << LIBRARYDIRECTORIES << "/fulllibs/fulllibrary_" << (int)n << "_" << i << ".bin";
            fsfile.open(filename.str().c_str(), std::ios::in | std::ios::binary);
            filename.str("");
            fsfile.seekg(0, std::ios::end);
            size = fsfile.tellg();
            fsfile.seekg(0, std::ios::beg);
            size /= 8;
            fs[i].resize(size);
            for(size_t j = 0;  j < size; j++)
                fsfile.read((char*)&fs[i][j], sizeof(UInt64));

            fsfile.close();
        }


        while(true) {
            UInt64 nf;
            MPI_Recv(&nf, 1, MPI_UNSIGNED_LONG_LONG, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &state);
            if (state.MPI_TAG == 1) {
                UInt64 lpf = nf >> 32;
                UInt64 f = nf & 0xFFFFFFFF;
                UInt64 lpfpf = lpf ^ f;

                UInt64 fsi;
                UInt8 ts1, ts2, ts3, ts = fComplication;
                for(size_t i = 0; i < 5 && ts == fComplication; i++) {
                    for(size_t j = 0; j < fs[i].size(); j++) {
                        fsi = fs[i][j];
                        nllib.get(fsi ^ lpf, ts1);
                        nllib.get(fsi ^ f, ts2);
                        if (ts1 + ts2 + i < ts) {
                            ts = ts1 + ts2 + i;
                            break;
                        }
                        nllib.get(fsi ^ lpfpf, ts3);
                        if (ts1 + ts3 + i < ts) {
                            ts = ts1 + ts3 + i;
                            break;
                        }
                        if (ts2 + ts3 + i < ts) {
                            ts = ts2 + ts3 + i;
                            break;
                        }
                    }
                }

                MPI_Send(&ts, 1, MPI_UNSIGNED_CHAR, 0, 3, MPI_COMM_WORLD);
            } else {
                std::cout << "Process " << std::dec << rank << " ending work." << std::endl;
                break;
            }
        }
    }

    MPI_Finalize();
    
    return (0);
}
